package cn.jsc.user.service.impl;

import cn.jsc.basic.exception.BusinessException;
import cn.jsc.basic.service.impl.BaseServiceImpl;
import cn.jsc.basic.util.AjaxResult;
import cn.jsc.basic.util.HttpClientUtils;
import cn.jsc.basic.util.MD5Utils;
import cn.jsc.basic.util.StrUtils;
import cn.jsc.user.constant.UserConstant;
import cn.jsc.user.constant.WxConstants;
import cn.jsc.user.domain.LoginInfo;
import cn.jsc.user.domain.User;
import cn.jsc.user.domain.WxUser;
import cn.jsc.user.dto.LoginDto;
import cn.jsc.user.mapper.LoginInfoMapper;
import cn.jsc.user.mapper.UserMapper;
import cn.jsc.user.mapper.WxUserMapper;
import cn.jsc.user.service.ILoginInfoServiceI;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 服务层
 */
@Service
public class LoginInfoServiceImpl extends BaseServiceImpl<LoginInfo> implements ILoginInfoServiceI {

    @Autowired
    private LoginInfoMapper loginInfoMapper;

    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate redisTemplate;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private WxUserMapper wxUserMapper;

    @Override
    public AjaxResult accountLogin(LoginDto loginDto) {
        //校验
        String account = loginDto.getAccount();
        String password = loginDto.getPassword();
        Integer type = loginDto.getType();
        //空值校验
        if(StringUtils.isEmpty(account) || StringUtils.isEmpty(password) ){
            throw new BusinessException("信息不可以为空！！！");
        }
        //用户名校验
        LoginInfo loginInfo = loginInfoMapper.findByAccount(loginDto);
        if(loginInfo==null){
            throw new BusinessException("用户名或密码错误！！！");
        }
        //密码校验
        String salt = loginInfo.getSalt();
        String md5Pwd = MD5Utils.encrypByMd5(password + salt);
        if(!md5Pwd.equals(loginInfo.getPassword())){
            throw new BusinessException("用户名或密码错误！！！");
        }
        //状态校验
        if(!loginInfo.getDisable()){
            throw new BusinessException("该账号已被禁用，请联系管理员！");
        }

        //2.将token和登录信息对象 保存在 redis 30分钟有效
        //登陆成功：将登陆信息对象放在session中 - redis，并设置30分钟过期
        //使用UUID生成36位的随机字符串作为token
        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(token,loginInfo,30, TimeUnit.MINUTES);

        //将token【jessionid】 和 loginInfo登陆信息对象传递给前端
        Map<String,Object> map = new HashMap<>();
        map.put("token",token);//用途 - 做拦截
        //设置密码和盐值为null -安全
        loginInfo.setPassword(null);
        loginInfo.setSalt(null);
        map.put("loginInfo",loginInfo);//用途 - 显示用户信息
        //返回到AjaxResult中的ResultObj传递到前端
        return AjaxResult.me().setResultObj(map);

    }

    /**
     * 微信登录
     * @param code
     * @return
     */
    @Override
    public AjaxResult wechatLogin(String code) {
        //通过code发送第二个请求: - HttpClientUtil类
        String url = WxConstants.GET_ACK_URL.replace("APPID", WxConstants.APPID)
                .replace("SECRET", WxConstants.SECRET)
                .replace("CODE", code);
        String jsonStr = HttpClientUtils.httpGet(url);

        //返回json格式的字符串：【access_token,openid】
        //通过阿里的fastJson将返回json格式的字符串 转成 JSON对象： 为了获取【access_token,openid】
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        String access_token = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");
        //通过openid查询t_wxuser表
        WxUser wxUser=wxUserMapper.findByOpenid(openid);
        //不为null - 你以前登陆过，绑定过 - 免密登陆
        //如果有微信用户信息  并且 已经与t_user进行绑定 = 不是第一次登陆-免密登陆
        if(wxUser!=null && wxUser.getUser_id()!=null){
            Long user_id = wxUser.getUser_id();
            User user = userMapper.loadById(user_id);
            LoginInfo loginInfo = loginInfoMapper.loadById(user.getLogininfo_id());

            //2.将token和登录信息对象 保存在 redis 30分钟有效
            //登陆成功：将登陆信息对象放在session中 - redis，并设置30分钟过期
            //使用UUID生成36位的随机字符串作为token
            String token = UUID.randomUUID().toString();
            redisTemplate.opsForValue().set(token,loginInfo,30, TimeUnit.MINUTES);

            //将token【jessionid】 和 loginInfo登陆信息对象传递给前端
            Map<String,Object> map = new HashMap<>();
            map.put("token",token);//用途 - 做拦截
            //设置密码和盐值为null -安全
            loginInfo.setPassword(null);
            loginInfo.setSalt(null);
            map.put("loginInfo",loginInfo);//用途 - 显示用户信息
            //返回到AjaxResult中的ResultObj传递到前端
            return AjaxResult.me().setResultObj(map);
        }else {//为null - 没有绑定 - 跳转页面进行绑定微信用户 - 第一次用微信登陆，将access_token,openid传递到页面进行微信用户绑定
            Map<String,Object> map = new HashMap<>();
            map.put("access_token",access_token);//用途 - 做拦截
            map.put("openid",openid);//用途 - 显示用户信息
            //返回到AjaxResult中的ResultObj传递到前端
            return AjaxResult.me().setSuccess(false).setResultObj(map);
        }
    }

    /**
     * 微信绑定  添加t_wxuser对象   与  user_id绑定
     * @param map
     * @return
     */
    @Override
    public AjaxResult wechatBinder(Map<String, String> map) {
        String phone = map.get("phone");
        String verifyCode = map.get("verifyCode");
        String access_token = map.get("access_token");
        String openid = map.get("openid");
        //1.校验
        //1.1.空值校验
        if(StringUtils.isEmpty(phone) || StringUtils.isEmpty(verifyCode)){
            throw new BusinessException("信息不能为空！！！");
        }
        //1.2.验证码是否过期 - 或失效 //key=binder:phone   value=code:时间戳
        Object obj = redisTemplate.opsForValue().get(UserConstant.BINDER_CODE_PREFIX + phone);
        if(obj==null){
            throw new BusinessException("验证码已失效！！！");
        }
        //1.3.验证码是否正确 - 传递过来的和redis中的
        //key - 业务键:手机    value - code:时间戳
        String redisVerifyCode = obj.toString().split(":")[0];
        if(!verifyCode.equalsIgnoreCase(redisVerifyCode)){
            throw new BusinessException("验证码不正确！！！");
        }
        //获取t_wxuser对象
        //发送第三个请求-获取t_wxuser对象
        String path = WxConstants.GET_USER_URL.replace("ACCESS_TOKEN", access_token)
                .replace("OPENID", openid);
        //发送请求，返回json结果
        String jsonStr = HttpClientUtils.httpGet(path);
        //将返回的json结果存入到wxuser表中
        WxUser wxUser=jsonStr2WxUser(jsonStr);
        //通过phone查询用户是否存在
        User user = userMapper.loadByPhone(phone);
        //user表中用户不存在
        if(user==null){
            //创建user对象 - phone
             user=phone2User(phone);
            //创建logininfo对象
            LoginInfo loginInfo=user2LoginInfo(user);
            //保存LoginInfo对象 - 自增长id
            loginInfoMapper.save(loginInfo);
            //先把Logininfo_id存入user表中
            user.setLogininfo_id(loginInfo.getId());
            userMapper.save(user);
        }
        //user表中用户存在，存入user_id到wxuser表中
        //校验账号是否被禁用
        if(user.getState()==0){
            throw new BusinessException("您的账号已被禁用，请联系管理员!!!");
        }
        wxUser.setUser_id(user.getId());
        //把user_id保存到wxuser表中
        wxUserMapper.save(wxUser);

        //免密登录
        LoginInfo loginInfo = loginInfoMapper.loadById(user.getLogininfo_id());
        //2.将token和登录信息对象 保存在 redis 30分钟有效
        //登陆成功：将登陆信息对象放在session中 - redis，并设置30分钟过期
        //使用UUID生成36位的随机字符串作为token
        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(token,loginInfo,30, TimeUnit.MINUTES);
        //将token【jessionid】 和 loginInfo登陆信息对象传递给前端
        Map<String,Object> mapTem = new HashMap<>();
        mapTem.put("token",token);//用途 - 做拦截
        //设置密码和盐值为null -安全
        loginInfo.setPassword(null);
        loginInfo.setSalt(null);
        mapTem.put("loginInfo",loginInfo);//用途 - 显示用户信息
        //返回到AjaxResult中的ResultObj传递到前端
        return AjaxResult.me().setResultObj(mapTem);
    }

    /**
     * 将发送的请求返回的json结果添加到wxuser表中
     * {
     * "openid":"OPENID",
     * "nickname":"NICKNAME",
     * "sex":1,
     * "province":"PROVINCE",
     * "city":"CITY",
     * "country":"COUNTRY",
     * "headimgurl": "https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
     * "privilege":[
     * "PRIVILEGE1",
     * "PRIVILEGE2"
     * ],
     * "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
     *
     * }
     * @param jsonStr
     * @return
     */
    private WxUser jsonStr2WxUser(String jsonStr) {
        WxUser wxUser = new WxUser();
        //通过阿里的fastJson将返回json格式的字符串 转成 JSON对象：
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        wxUser.setOpenid(jsonObject.getString("openid"));
        wxUser.setNickname(jsonObject.getString("nickname"));
        wxUser.setSex(jsonObject.getInteger("sex"));
        wxUser.setHeadimgurl(jsonObject.getString("headimgurl"));
        wxUser.setUnionid(jsonObject.getString("unionid"));
        String country = jsonObject.getString("country");//国家
        String province = jsonObject.getString("province");//省份
        String city = jsonObject.getString("city");//城市
        wxUser.setAddress(country+province+city);
        return wxUser;
    }

    /**
     * 没有use用户不存在就通过phone创建user对象
     * @param phone
     * @return
     */
    private User phone2User(String phone) {
        User user = new User();
        user.setUsername(phone);
        user.setPhone(phone);
        //给一个随机密码 - 微信登陆成功之后在个人中心可以修改密码
        String salt = StrUtils.getComplexRandomString(32);
        String pwd = StrUtils.getRandomString(6);
        String md5Pwd = MD5Utils.encrypByMd5(pwd + salt);
        user.setSalt(salt);
        user.setPassword(md5Pwd);
        return user;
    }

    /**
     * 要级联logininfo表，创建logininfo对象
     * @param user
     * @return
     */
    private LoginInfo user2LoginInfo(User user) {
        LoginInfo loginInfo = new LoginInfo();
        BeanUtils.copyProperties(user,loginInfo);
        loginInfo.setType(1);//微信登录，1表示用户
        return loginInfo;
    }
}
